﻿using EasyXaf.LowCode.RulesEngineEditors.Models;
using EasyXaf.LowCode.RulesEngineEditors.UndoManagers;
using Newtonsoft.Json;

namespace EasyXaf.LowCode.RulesEngineEditors;

public static class WorkflowActions
{
    public static void ExpandOrCollapseChildRules(this IUndoManager undoManager, RuleObject rule)
    {
        using (undoManager.CreateTransaction("展开或折叠子节点"))
        {
            undoManager.Execute(new
            {
                rule.Workflow,
                rule.Path,
                rule.IsCollapse
            }, state =>
            {
                var rule = state.Workflow.FindRule(state.Path);
                rule.IsCollapse = !state.IsCollapse;
                return new
                {
                    state.Workflow,
                    state.Path,
                    IsCollapse = !state.IsCollapse
                };
            });
        }
    }

    public static void ChangeRuleOrder(this IUndoManager undoManager, RuleObject rule, int newIndex)
    {
        using (undoManager.CreateTransaction("改变规则顺序"))
        {
            var parentRule = rule.Parent;
            var rules = parentRule == null ? rule.Workflow.Rules : parentRule.Rules;
            undoManager.Execute(new
            {
                rule.Workflow,
                ParentPath = parentRule?.Path,
                OldIndex = rules.IndexOf(rule),
                NewIndex = newIndex
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                (rules[state.NewIndex], rules[state.OldIndex]) = (rules[state.OldIndex], rules[state.NewIndex]);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return new
                {
                    state.Workflow,
                    state.ParentPath,
                    OldIndex = state.NewIndex,
                    NewIndex = state.OldIndex
                };
            });
        }
    }

    public static void ChangeRuleOperator(this IUndoManager undoManager, RuleObject rule, RuleOperator newOperator)
    {
        using (undoManager.CreateTransaction("更改规则操作符"))
        {
            undoManager.Execute(new
            {
                rule.Workflow,
                rule.Path,
                OldOperator = rule.Operator,
                NewOperator = newOperator
            }, state =>
            {
                var rule = state.Workflow.FindRule(state.Path);
                rule.Operator = state.NewOperator;
                rule.Validate();

                return new
                {
                    state.Workflow,
                    state.Path,
                    OldOperator = state.NewOperator,
                    NewOperator = state.OldOperator
                };
            });
        }
    }

    public static void ChangeNestedRuleOutputMode(this IUndoManager undoManager, RuleObject rule, NestedRuleOutputMode newMode)
    {
        using (undoManager.CreateTransaction("更改嵌套规则输出模式"))
        {
            undoManager.Execute(new
            {
                rule.Workflow,
                rule.Path,
                OldMode = rule.NestedRuleOutputMode,
                NewMode = newMode
            }, state =>
            {
                var rule = state.Workflow.FindRule(state.Path);
                rule.NestedRuleOutputMode = state.NewMode;
                rule.Validate();

                return new
                {
                    state.Workflow,
                    state.Path,
                    OldMode = state.NewMode,
                    NewMode = state.OldMode
                };
            });
        }
    }

    public static void AddRule(this IUndoManager undoManager, RuleObject rule, RuleObject parentRule, int index, WorkflowObject workflow)
    {
        using (undoManager.CreateTransaction("添加规则"))
        {
            undoManager.Execute(new
            {
                Workflow = workflow,
                Json = rule.ToJson(),
                ParentPath = parentRule?.Path,
                Index = index
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                var newRule = RuleObject.FromJson(state.Json, parentRule, state.Workflow);
                rules.Insert(state.Index, newRule);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                rules.RemoveAt(state.Index);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            });
        }
    }

    public static void UpdateRule(this IUndoManager undoManager, RuleObject oldRule, RuleObject newRule)
    {
        using (undoManager.CreateTransaction("更新规则"))
        {
            var parentRule = oldRule.Parent;
            var rules = parentRule == null ? oldRule.Workflow.Rules : parentRule.Rules;
            undoManager.Execute(new
            {
                oldRule.Workflow,
                OldJson = oldRule.ToJson(),
                NewJson = newRule.ToJson(),
                ParentPath = parentRule?.Path,
                Index = rules.IndexOf(oldRule)
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                rules[state.Index] = RuleObject.FromJson(state.NewJson, parentRule, state.Workflow);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return new
                {
                    state.Workflow,
                    OldJson = state.NewJson,
                    NewJson = state.OldJson,
                    state.ParentPath,
                    state.Index
                };
            });
        }
    }

    public static void DeleteRule(this IUndoManager undoManager, RuleObject rule)
    {
        using (undoManager.CreateTransaction("删除规则"))
        {
            var parentRule = rule.Parent;
            var rules = parentRule == null ? rule.Workflow.Rules : parentRule.Rules;
            undoManager.Execute(new
            {
                rule.Workflow,
                Json = rule.ToJson(),
                ParentPath = parentRule?.Path,
                Index = rules.IndexOf(rule)
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                rules.RemoveAt(state.Index);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                var rule = RuleObject.FromJson(state.Json, parentRule, state.Workflow);
                rules.Insert(state.Index, rule);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            });
        }
    }

    public static void AddChildRule(this IUndoManager undoManager, RuleObject rule, RuleObject childRule)
    {
        using (undoManager.CreateTransaction("添加子规则"))
        {
            undoManager.Execute(new
            {
                rule.Workflow,
                RulePath = rule.Path,
                ChildJson = childRule.ToJson(),
                Index = rule.Rules.Count
            }, state =>
            {
                var rule = state.Workflow.FindRule(state.RulePath);
                var childRule = RuleObject.FromJson(state.ChildJson, rule, state.Workflow);
                rule.Rules.Insert(state.Index, childRule);
                rule.Validate();

                return state;
            }, state =>
            {
                var rule = state.Workflow.FindRule(state.RulePath);
                rule.Rules.RemoveAt(state.Index);
                rule.Validate();

                return state;
            });
        }
    }

    public static void AddParentRule(this IUndoManager undoManager, RuleObject rule, RuleObject newParentRule)
    {
        using (undoManager.CreateTransaction("添加父规则"))
        {
            var parentRule = rule.Parent;
            var rules = parentRule == null ? rule.Workflow.Rules : parentRule.Rules;

            undoManager.Execute(new
            {
                rule.Workflow,
                NewParentJson = newParentRule.ToJson(),
                ParentPath = parentRule?.Path,
                Index = rules.IndexOf(rule)
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var index = state.Index;
                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;

                var newParentRule = RuleObject.FromJson(state.NewParentJson, parentRule, state.Workflow);
                var rule = rules[index];
                rule.Parent = newParentRule;
                newParentRule.Rules.Add(rule);

                rules.RemoveAt(index);
                rules.Insert(index, newParentRule);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var index = state.Index;
                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                var rule = rules[index].Rules.First();
                rule.Parent = parentRule;

                rules.RemoveAt(index);
                rules.Insert(index, rule);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            });
        }
    }

    public static void AddParentForRules(this IUndoManager undoManager, RuleObject rule, RuleObject newParentRule)
    {
        using (undoManager.CreateTransaction("添加父规则"))
        {
            var parentRule = rule.Parent;
            var rules = parentRule == null ? rule.Workflow.Rules : parentRule.Rules;

            undoManager.Execute(new
            {
                rule.Workflow,
                NewParentJson = newParentRule.ToJson(),
                ParentPath = parentRule?.Path,
                Index = rules.IndexOf(rule)
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var index = state.Index;
                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                var newParentRule = RuleObject.FromJson(state.NewParentJson, parentRule, state.Workflow);

                if (parentRule != null)
                {
                    foreach (var childRule in parentRule.Rules)
                    {
                        childRule.Parent = newParentRule;
                        newParentRule.Rules.Add(childRule);
                    }

                    rules.Clear();
                    rules.Add(newParentRule);
                }
                else
                {
                    var rule = rules[index];
                    rule.Parent = newParentRule;
                    newParentRule.Rules.Add(rule);

                    rules.RemoveAt(index);
                    rules.Insert(index, newParentRule);
                }

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var index = state.Index;
                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                var childRules = rules[index].Rules;

                rules.RemoveAt(index);

                foreach (var childRule in childRules)
                {
                    childRule.Parent = parentRule;
                    rules.Insert(index++, childRule);
                }

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            });
        }
    }

    public static void DeleteAndPreserveChildRules(this IUndoManager undoManager, RuleObject rule)
    {
        using (undoManager.CreateTransaction("删除并保留子规则"))
        {
            var parentRule = rule.Parent;
            var rules = parentRule == null ? rule.Workflow.Rules : parentRule.Rules;

            undoManager.Execute(new
            {
                rule.Workflow,
                Json = rule.ToJson(),
                ParentPath = parentRule?.Path,
                Index = rules.IndexOf(rule)
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var index = state.Index;
                var rule = RuleObject.FromJson(state.Json, parentRule, state.Workflow);

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                rules.RemoveAt(index);

                foreach (var childRule in rule.Rules)
                {
                    childRule.Parent = parentRule;
                    rules.Insert(index++, childRule);
                }

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            }, state =>
            {
                RuleObject parentRule = null;
                if (!string.IsNullOrWhiteSpace(state.ParentPath))
                {
                    parentRule = state.Workflow.FindRule(state.ParentPath);
                }

                var rules = parentRule == null ? state.Workflow.Rules : parentRule.Rules;
                var rule = RuleObject.FromJson(state.Json, parentRule, state.Workflow);

                var index = state.Index;
                for (var i = 0; i < rule.Rules.Count; i++)
                {
                    rules.RemoveAt(index);
                }

                rules.Insert(index, rule);

                if (parentRule != null)
                {
                    parentRule.Validate();
                }
                else
                {
                    state.Workflow.Validate();
                }

                return state;
            });
        }
    }

    public static void UpdateWorkflow(this IUndoManager undoManager, WorkflowObject workflow, WorkflowObject newWorkflow)
    {
        UpdateWorkflow(undoManager, workflow, newWorkflow.ToWorkflow());
    }

    public static void UpdateWorkflow(this IUndoManager undoManager, WorkflowObject workflow, RulesEngineWorkflow newWorkflow)
    {
        using (undoManager.CreateTransaction("更新工作流"))
        {
            undoManager.Execute(new
            {
                Workflow = workflow,
                OldJson = JsonConvert.SerializeObject(workflow.ToWorkflow()),
                NewJson = JsonConvert.SerializeObject(newWorkflow)
            }, state =>
            {
                var rulesEngineWorkflow = JsonConvert.DeserializeObject<RulesEngineWorkflow>(state.NewJson);
                state.Workflow.UpdateWorkflow(rulesEngineWorkflow);
                state.Workflow.Validate();
                return new
                {
                    state.Workflow,
                    OldJson = state.NewJson,
                    NewJson = state.OldJson
                };
            });
        }
    }
}
